package com.example.demo.config;

import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;

/**
 * @version 1.0
 * @Author: LJP
 * @Description: mybatis的%和_模糊查询处理
 * @Date: Created in 15:34 2021/12/17
 */
@Component
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MybatisInterceptor implements Interceptor {

    @Override
    @SuppressWarnings("unused")
    public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        //通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性;：MetaObject是Mybatis提供的一个用于方便、
        //优雅访问对象属性的对象，通过它可以简化代码、不需要try/catch各种reflect异常，同时它支持对JavaBean、Collection、Map三种类型对象的操作。
        MetaObject metaObject = MetaObject
                .forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                        new DefaultReflectorFactory());
        //先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        //id为执行的mapper方法的全路径名，如com.uv.dao.UserMapper.insertUser
        String id = mappedStatement.getId();
        //sql语句类型 select、delete、insert、update
        String sqlCommandType = mappedStatement.getSqlCommandType().toString();

        BoundSql boundSql = statementHandler.getBoundSql();
        //获取到原始sql语句
        String sql = boundSql.getSql();

        //如果是个查询且是个模糊查询则处理sql
        if (sqlCommandType.equals("SELECT") && sql.contains("LIKE")) {
            //参数所在对象-->parameterObject
            Object parameterObject = boundSql.getParameterObject();
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

            Configuration configuration = mappedStatement.getConfiguration();
            MetaObject metaObject2 = configuration.newMetaObject(parameterObject);

            for (ParameterMapping parameterMapping : parameterMappings) {
                String propertyName = parameterMapping.getProperty();
                if (metaObject2.hasGetter(propertyName)) {
                    Object obj = metaObject2.getValue(propertyName);
                    String parameter = obj.toString();

                    if (checkParameter(parameter)) {
                        String str = chekStrMed(parameter);
                        boundSql.setAdditionalParameter(propertyName, str);
                    } else {
                        boundSql.setAdditionalParameter(propertyName, obj);
                    }
                } else if (boundSql.hasAdditionalParameter(propertyName)) {
                    Object obj = boundSql.getAdditionalParameter(propertyName);
                    String parameter = obj.toString();

                    if (checkParameter(parameter)) {
                        String str = chekStrMed(parameter);
                        boundSql.setAdditionalParameter(propertyName, str);
                    } else {
                        boundSql.setAdditionalParameter(propertyName, obj);
                    }
                }
            }

        }
        return invocation.proceed();
    }


    /**
     * Chek str med string.
     *
     * @param str the str
     * @return the string
     */
    public String chekStrMed(String str) {
        boolean start = StrUtil.startWith(str, "%");
        boolean end = StrUtil.endWith(str, "%");

        Function<String, Boolean> check = m -> m.equals("%") || m.equals("_");

        Function<String, String> transformString = item -> {
            StringBuilder builder = new StringBuilder(item);
            if (item.contains("%") && item.contains("_")) {
                builder.insert(Math.min(item.indexOf("%"), item.indexOf("_")), "\\");
                builder.insert(0, "%");
                builder.insert(builder.length(), "%");
                return builder.toString();
            } else if (item.contains("%")) {
                builder.insert(item.indexOf("%"), "\\");
                builder.insert(0, "%");
                builder.insert(builder.length(), "%");
                return builder.toString();
            } else if (item.contains("_")) {
                builder.insert(item.indexOf("_"), "\\");
                builder.insert(0, "%");
                builder.insert(builder.length(), "%");
                return builder.toString();
            }
            return item;
        };

        Function<String, Map<Boolean, String>> checkStr = item -> {
            HashMap<Boolean, String> map = new HashMap<>();
            item = StrUtil.removePrefix(item, "%");
            item = StrUtil.removeSuffix(item, "%");
            String apply = transformString.apply(item);
            if (!apply.equals(item)) {
                map.put(true, apply);
            }
            return map;
        };

        if (start && end) {
            //包含
            Map<Boolean, String> map = checkStr.apply(str);
            if (StrUtil.isNotBlank(map.get(true))) {
                return map.get(true);
            }
        } else if (start) {
            //以%开头
            String tempStr = StrUtil.removePrefix(str, "%");
            str = check.apply(tempStr) ? transformString.apply(str) : str;
        } else if (end) {
            //以%结尾
            String tempStr = StrUtil.removeSuffix(str, "%");
            str = check.apply(tempStr) ? transformString.apply(str) : str;
        }
        return str;
    }

    /**
     * Check parameter boolean.
     *
     * @param str the str
     * @return the boolean
     */
    public boolean checkParameter(String str) {
        String patternOne = "^%.*";
        String patternTwo = ".*%$";
        String patternThree = "^%.*%$";

        Pattern compileOne = Pattern.compile(patternOne);
        Pattern compileTwo = Pattern.compile(patternTwo);
        Pattern compileThree = Pattern.compile(patternThree);

        return compileOne.matcher(str).matches() || compileTwo.matcher(str).matches() || compileThree.matcher(str).matches();
    }

}
